﻿//////////////////////////////////////////////////////////////////////////////
//
//  Copyright © 1998-2024 Glodon Company Limited.  All rights reserved.
//
//  Use of this software is subject to the terms of the Glodon license
//  agreement provided at the time of installation or download, or which
//  otherwise accompanies this software in either electronic or hard copy form.  
//
//////////////////////////////////////////////////////////////////////////////

// Reviewed
#pragma once

#include "GcmpModelInterface.h"
#include "OwnerPtr.h"
#include <string>
#include "INoElementIdDbObject.h"
#include "IDocument.h"

namespace gcmp
{
    
    class IPasteHelper;
    class ICopyContext;
    class ICopyStrategyReporter;
    class IDocument;
    class IElement;

    /// \brief 复制粘贴行为接口
    class GCMP_MODEL_INTERFACE_EXPORT IElementCopyPasteComponent : public INoElementIdDbObject
    {
        DEFINE_CAST_DERIVED(gcmp::IElementCopyPasteComponent, gcmp::INoElementIdDbObject)
    public:
        /// \brief 创建空的拷贝粘贴行为。
        ///
        /// 此行为创建一个空的拷贝粘贴行为。
        /// \param pElement 所属Element
        /// \return OwnerPtr<IElementCopyPasteComponent> 拷贝粘贴行为
        static OwnerPtr<IElementCopyPasteComponent> CreateNullCopyPasteComponent();

        /// \brief 创建默认的拷贝粘贴行为。
        ///
        /// 此行为允许所属Element被拷贝，同时考虑了所属Element的ExternalData的拷贝粘贴行为。
        /// \param pElement 所属Element
        /// \return OwnerPtr<IElementCopyPasteComponent> 拷贝粘贴行为
        static OwnerPtr<IElementCopyPasteComponent> CreateCopyWithExternalDataCopyPasteComponent();

    public:
        /// \brief 获取粘贴助手
        ///
        /// \return OwnerPtr<IPasteHelper>
        virtual OwnerPtr<IPasteHelper> GetPasteHelper(void) const = 0;

        /// \brief 复制粘贴后更新操作
        ///
        /// 复制粘贴后需要的后处理可以放在这里，比如：对被复制粘贴后的元素进行重新命名
        /// \param copyContext 拷贝上下文
        /// \return void
        virtual void UpdateAfterCopy(const ICopyContext& copyContext) = 0;

        /// \brief 当Weak Parent被拷贝但此Element没有同时被拷贝时，此函数被调用
        ///
        /// 调用时机：当Weak Parent被拷贝但此Element没有同时被拷贝时，此Element会通过此接口收到通知。 
        /// 用途：一般用来处理Element存在Id容器的情况。 
        /// 例子：当Sketch里的对象被拷贝但Sketch本身没被拷贝时，Sketch通过实现此接口将拷贝的新对象加到SketchObjectId容器中。 
        ///
        /// \param pDocumentFrom 源文档
        /// \param pSource 被拷贝的IElement指针
        /// \param pCopy 拷贝得到的IElement指针
        /// \return void
        virtual void UpdateForWeakParentCopied(
            const IDocument* pDocumentFrom, 
            const IElement* pSource, 
            const IElement* pCopy
        ) const = 0;

        /// \brief 决定是否需要一起被拷贝
        ///
        /// 调用时机：当一个对象是否拷贝不仅仅取决于一个对象时，无法通过Report来直接决定是否拷贝。 
        /// 用途：根据待拷贝对象集来决定自己是否拷贝 
        /// 例子：墙连接取决于连接的两个墙、尺寸约束取决于约束的对象 
        ///
        /// \param copyContext 拷贝上下文
        /// \return bool 返回true表示需要，false表示不需要。
        virtual bool ShouldBeCopiedTogether(const ICopyContext & copyContext) const = 0;

        /// \brief 跨文档拷贝时判断StrongParents是否需要被拷贝
        ///
        /// 调用时机：跨文档拷贝时判断StrongParents是否需要被拷贝。 
        ///
        /// \return bool 返回true表示需要，false表示不需要。
        virtual bool IsStrongParentsNeedCopied(void) const = 0;

        /// \brief 判断是否可以复制
        /// \param pErrorMessage 输出信息
        /// \return bool 返回true表示可以复制，false表示不能复制。
        virtual bool CanBeCopied(std::wstring* pErrorMessage) const = 0;
    };

    /// \brief IElementCopyPasteComponent的工具类
    ///
    /// 提供创建具体IElementCopyPasteComponent派生类的模板函数
    class IElementCopyPasteComponentUtils
    {
    public:
        /// \brief 创建IElementCopyPasteComponent派生类
        ///
        /// \param pDocument 文档指针
        /// \param ownerId 所在Element的Id
        /// \return OwnerPtr<IElementCopyPasteComponent> 创建出的对象
        template <typename T>
        static OwnerPtr<IElementCopyPasteComponent> Create(IDocument *pDocument, const ElementId & ownerId)
        {
            OwnerPtr<T> opCopyPasteComponent = NEW_AS_OWNER_PTR(T);
            if (opCopyPasteComponent.get() && 
                nullptr != pDocument && ownerId.IsValid())
            {
                opCopyPasteComponent->SetOwnerElement(pDocument->GetElement(ownerId));
            }
            return TransferOwnership(opCopyPasteComponent);
        }
    };

    /// \brief 报告拷贝策略组件接口
    class GCMP_MODEL_INTERFACE_EXPORT IElementCopyStrategyComponent : public INoElementIdDbObject
    {
        DEFINE_CAST_DERIVED(gcmp::IElementCopyStrategyComponent, gcmp::INoElementIdDbObject)
    public:
        /// \brief 创建默认的报告拷贝策略组件
        ///
        /// 将关联平面、联结、参数绑定、图形、ExternalData等同时报告出来
        /// \param pElement 所属Element
        /// \return OwnerPtr<IElementCopyPasteComponent> 报告拷贝策略组件
        static OwnerPtr<IElementCopyStrategyComponent> CreateReportCoreComponentsCopyStrategyComponent();

        /// \brief 创建空的报告拷贝策略组件
        ///
        /// 不报告任何内容
        /// \param pElement 所属Element
        /// \return OwnerPtr<IElementCopyPasteComponent> 报告拷贝策略组件
        static OwnerPtr<IElementCopyStrategyComponent> CreateNullCopyStrategyComponent();

    public:
        /// \brief 报告引用的Element的id为拷贝、保持、放弃或者询问
        ///
        /// 假设Element A引用了Element B的id，只拷贝A得到A'，则当B
        /// 1. 报告为拷贝：拷贝B得到B'，A'中引用的B的Id替换成B'的Id。
        /// 2. 报告为保持：不拷贝B，A'中引用的B的ElementId不变。
        /// 3. 报告为放弃：不拷贝B，为放弃引用的A'中的B的id，将B的id作为参数，调用A'的UpdateForWeakParentDeletion方法。\param n1 参数1说明
        ///
        /// \param copyContext 拷贝上下文
        /// \param reporter 拷贝策略报告器
        /// \return void
        /// \see 关于如何报告详见: http://pm.glodon.com/wiki/pages/viewpage.action?pageId=64808043
        virtual void Report(const gcmp::ICopyContext & copyContext, gcmp::ICopyStrategyReporter& reporter) const = 0;
    };

    /// \brief IElementCopyStrategyComponent的工具类
    ///
    /// 提供创建具体IElementCopyStrategyComponent派生类的模板函数
    class IElementCopyStrategyComponentUtils
    {
    public:
        /// \brief 创建IElementCopyStrategyComponent派生类
        ///
        /// \param pDocument 文档指针
        /// \param ownerId 所在Element的Id
        /// \return OwnerPtr<IElementCopyStrategyComponent> 创建出的对象
        template <typename T>
        static OwnerPtr<IElementCopyStrategyComponent> Create(IDocument *pDocument, const ElementId & ownerId)
        {
            OwnerPtr<T> opCopyStrategyComponent = NEW_AS_OWNER_PTR(T, nullptr);
            if (nullptr != pDocument && ownerId.IsValid())
            {
                IElement * pOwnerElement = pDocument->GetElement(ownerId);
                opCopyStrategyComponent->SetOwnerElement(pOwnerElement);
            }
            return TransferOwnership(opCopyStrategyComponent);
        }
    };
}// namespace gcmp


